/* Monom.cpp  file
   ---------------
   Implementation of the Monom class methods

 */

#include <iostream>
#include <string>       // used for 4 digits after precision point
#include <sstream>      // used for 4 digits after precision point
#include <math.h>       // used for pow(x,y) function
#include <assert.h>     // assert error handler
#include "Monom.h"


using namespace std;

// cout << operator
ostream& operator<<(ostream &out, const Monom &m)
{
    /* Flags are used in order to decide what
       exactly will be printed on the screen
     */

    int printInt = 0;    /* 0 = doesn't have fraction part
                            1 = does    have fraction part
                          */

    int printC = 1;      /* 0 = don't print C coefficient
                            1 =       print C coefficient
                          */

    int printX = 2;      /* 0 = don't print X
                            1 =       print X
                            2 = print X with ^ - power sign
                          */

    // flag - determine whether num has fraction part
    if ((m.C - ((int) m.C)) == 0)
        printInt = 1;
    
    // X - should be printed or not ?
    if (m.C == 0)
        printX = 0;
    else {
        switch (m.m) {
            case 0:
                printX = 0;
                break;

            case 1:
                printX = 1;
                if (m.C == 1.0)
                    printC = 0;
                break;

            default:            // unnecessary
                if (m.C == 1.0)
                    printC = 0;
                printX = 2;
        }
    }

    if (printC == 1)
    {
        if (printInt == 1)
        {
            if (m.C == -1.0)
            {
                if (printX > 0)
                    out << "-";
                else
                    out << "-1";
            } else
                out << ((int) m.C);
        }
        else
            out << m.C;
    }

    if (printX == 1)
        out << "x";
    else if (printX == 2)
        out << "x^" << m.m;

    return out;
}

// arithmetic multiplication <double> * Monom
const Monom operator*(const double &num, const Monom &m2)
{
	Monom temp(m2);
	temp.C*= num;
	return temp;
}

// Constructor with default values (0,0)
Monom::Monom(const double C, const int m)
{
	this->C = C;
	if (C == 0)
		this->m = 0;
	else
		this->m = m;
}

// Copy Constructor
Monom::Monom(const Monom &m)
{
    this->C = m.C;
    this->m = m.m;
}

// assignment operator --> Monom = Monom
Monom& Monom::operator=(const Monom &m2)
{
    this->C = m2.C;
    this->m = m2.m;
	return (*this);
}


// method for derivative
const Monom& Monom::derive() {
    C*=m;
	if (C == 0)
		m=0;
	else
	    m-=1;
    return (*this);
}

void Monom::setC(const double &num)
{
	this->C = num;
}

// operator for != comparison
bool Monom::operator!=(const Monom &m2) const
{
    if (this->C != m2.C || this->m != m2.m)
        return true;

    return false;
}

// comparison == operator
bool Monom::operator==(const Monom &m2) const
{
    if (this->C != m2.C || this->m != m2.m)
        return false;

    return true;
}


// power operation: Monom^scalar
const Monom Monom::operator^(const int &num) const
{
    Monom temp(*this);
	temp.C = pow(temp.C,num);
	temp.m = temp.m * num;
//	temp.m = (int) pow((double)temp.m,num);
    return temp;
}


// arithmetic division: Monom / Monom
const Monom Monom::operator/(const Monom &m2) const
{
    Monom temp(*this);

    // Division by 0 ?
    assert(m2.C != 0);
    temp.C /= m2.C;
    temp.m -= m2.m;
    return temp;
}

// arithmetic division: Monom / scalar
const Monom Monom::operator/(const double &num) const
{
    Monom temp(*this);

    // Division by 0 ?
    assert(num != 0);
    temp.C /= num;
    return temp;
}

// arithmetic multiplication: Monom * Monom
const Monom Monom::operator*(const Monom &m2) const
{
    Monom temp(*this);
    temp.C *= m2.C;
    temp.m += m2.m;
    return temp;
}

// arithmetic multiplication: Monom * scalar
const Monom Monom::operator*(const double &num) const
{
    Monom temp(*this);
    temp.C *= num;
    return temp;
}
// arithmetic multiplication *= --> Monom*=Monom
void Monom::operator *=(const Monom &m2)
{
	this->C *= m2.C;
	this->m += m2.m;
}


// arithmetic addition: Monom + Monom
const Monom Monom::operator+(const Monom &m2) const
{
    Monom temp(*this);
    assert (this->m == m2.m);   // error handler
    temp.C += m2.C;
    return temp;
}

// arithmetic addition += --> Monom+= scalar
void Monom::operator+=(const double &num)
{
    assert(this->m == 0);
    this->C += num;
}

// arithmetic subtraction: Monom - Monom
const Monom Monom::operator-(const Monom &m2) const
{
    Monom temp(*this);
    assert (this->m == m2.m);   // error handler
    temp.C -= m2.C;
    return temp;
}


// operator for () C*num^m calculation
const string Monom::operator()(const double &num) const             
{
	double res;
	res = (C*pow(num,m));

	// stream part
	std::ostringstream os;                  // clever resolution for 4 digits problem :-)
	os.setf(ios::fixed,ios::floatfield);    // show floating point
	os.precision(4);                        // show 4 digits after precision point
	os << res;                              // output this result format to os stream
	std::string str = os.str();             // save result as string

	// return answer as string
	return str;
}
